package bigo.data;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * This class is used to store tha vectors that have sparse elements distribution
 * reviewed @ Dec 04
 * @author Song Liu (sl9885@bristol.ac.uk)
 */
public class HashVector extends Vector {

    private HashMap<Integer, Float> map = new LinkedHashMap<Integer, Float>();
    private int length;

    /**
     * Create a HashVector with 0 offset
     */
    public HashVector() {
        super(0);
    }

    /**
     * Create a HashVector with 0 offset, but with given size and length
     * @param size
     * @param length
     */
    public HashVector(int size, int length) {
        super(0);
        map = new LinkedHashMap<Integer, Float>(size);
        this.length = length;
    }

    /**
     * Create a HashVector with given offset, size and length, often used by creating
     * sub vector.
     * @param offset
     * @param size
     * @param length
     */
    public HashVector(int offset, int size, int length) {
        super(offset);
        map = new LinkedHashMap<Integer, Float>(size);
        this.length = length;
    }

    /**
     * How many elements are ACTUALLY stored in this vector
     * @return
     */
    public int size() {
        return map.size();
    }

    /**
     * Test if this vector empty, please note about the special case demostrated
     * below.
     * @return
     */
    public boolean isEmpty() {
        if (map.isEmpty()) {
            return true;
        } else {
            //this is another case for a empty row
            if (map.size() == 1
                    && map.containsKey(0)
                    && map.get(1) == 0) {
                return true;
            }
        }
        return false;
    }

    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }

    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }

    public Float get(Object key) {
        return map.get(key);
    }

    public Float put(Integer key, Float value) {
        return map.put(key, value);
    }

    public Float remove(Object key) {
        return remove(key);
    }

    public void putAll(Map<? extends Integer, ? extends Float> m) {
        map.putAll(m);
    }

    public void clear() {
        map.clear();
    }

    public Set<Integer> keySet() {
        return map.keySet();
    }

    public Collection<Float> values() {
        return map.values();
    }

    public Set<Entry<Integer, Float>> entrySet() {
        return map.entrySet();
    }

    @Override
    public Vector duplicate() {
        HashVector ret = new HashVector();
        ret.map = this.map;
        return ret;
    }

    /**
     * This method returns how many column does this vector SHOULD have if all filled up.
     * @return
     */
    @Override
    public int length() {
        return length;
    }

    @Override
    public String toString() {
        return map.toString();
    }

    public void write(DataOutput out) throws IOException {
        out.writeInt(size());
        out.writeInt(length());
        out.writeInt(getOffset());

        for (Integer i : keySet()) {
            out.writeInt(i);
            out.writeFloat(get(i));
        }
    }

    public void readFields(DataInput in) throws IOException {
        int size = in.readInt();
        map = new HashMap<Integer, Float>(size);
        this.length = in.readInt();
        setOffset(in.readInt());

        for (int i = 0; i < size; i++) {
            int col = in.readInt();
            float val = in.readFloat();
            put(col, val);
        }
    }

    @Override
    public Vector subVector(int from, int to) {
        //TODO, here may be a problem!
        HashVector ret = new HashVector(from,
                (int) ((to - from) * 0.75), to - from);
        for (Integer i : keySet()) {
            if (i >= from && i < to) {
                ret.put(i, get(i));
            }
        }
        return ret;
    }
}
